package com.gk.panda.gateway.router;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

@Component
public class DynamicRouteDefinition implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Autowired
    private RouteDefinitionWriter writer;//路由定义写入器

    //记录已经发布的路由信息
    private List<RouteDefinition> subscribeRouteDefinitions;

    @Value("${gateway.routes.serverAddress}")
    private String configAddress;

    @Value("${gateway.routes.namespace}")
    private String namespace;

    @Value("${gateway.routes.group}")
    private String group;

    @Value("${gateway.routes.name}")
    private String dataId;

//    public DynamicRouteDefinition(){
//        //初始化路由信息
//    }

    @PostConstruct
    public void init() throws NacosException {
        Properties props = new Properties();
        props.setProperty(PropertyKeyConst.SERVER_ADDR, configAddress);
        props.setProperty(PropertyKeyConst.NAMESPACE, namespace);
        ConfigService configService = NacosFactory.createConfigService(props);
        String routeInfo = configService.getConfig(dataId, group, 2000);
        System.out.println("初始化路由信息");
        subscribeRoutes(routeInfo);//初始化路由信息
        //这里得到定义的路由信息后，可以将路由信息使用路由信息写入器写入，作为初始化的路由信息
        configService.addListener(dataId, group, new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {
                System.out.println("更新路由信息");
                //这里的configInfo就是修改后的路由定义信息
                clearSubscribeRoutes();//先清理掉原来的路由
                //此时需要先将原来定义的路由信息删除掉，然后再将新的路由信息写入进去，从而达到路由跟新的效果
                subscribeRoutes(configInfo);
            }
        });
    }

    /**
     * 发布路由
     * @param routeInfo
     */
    public void subscribeRoutes(String routeInfo){
        System.out.println(routeInfo);
        List<RouteDefinition> routeDefinitions = JSONObject.parseArray(routeInfo, RouteDefinition.class);
        routeDefinitions.forEach(rd -> {
            //路由定义写入器将路由信息保存，然后发布出去
            writer.save(Mono.just(rd)).subscribe();
        });
        //应用时间发布器发布一个事件：路由刷新事件
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(writer));
        this.subscribeRouteDefinitions = routeDefinitions;
    }

    /**
     * 清理路由信息
     */
    public void clearSubscribeRoutes(){
        if(this.subscribeRouteDefinitions != null){
            this.subscribeRouteDefinitions.forEach(rd ->{
                //路由定义写入器将路由信息删除，然后发布出去
                writer.delete(Mono.just(rd.getId())).subscribe();
            });
            this.subscribeRouteDefinitions = null;
        }
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}